//
//  YMLogSwift.swift
//  swift_review
//
//  Created by Andrew on 2024/12/21.
//

import Foundation
 
public typealias Completion = ()->Void;
public typealias CrashCallback = (String,Completion)->Void;
 
public var crashCallBack: CrashCallback?
 
func signalHandler(signal: Int32) -> Void {
    print("Signal captured: \(signal)") // 添加调试信息
    let stackTrace = Thread.callStackSymbols.joined(separator: "\r\n")
    // 创建异常信息，手动配置
    let additionalInfo = """
    ======= EXCEPTION =======
    Signal: \(signal)
    Stack Trace:
    \(stackTrace)
    ========================
    """
        
    saveCrashLog(stackTrace, additionalInfo: additionalInfo) // 保存日志到本地
    crashCallBack?(stackTrace, {
        unregisterSignalHandler()
        exit(signal)
    })
}

func saveCrashLog(_ log: String, additionalInfo: String? = nil) {
    guard let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
        print("Failed to get document directory")
        return
    }
    let logFilePath = documentDirectory.appendingPathComponent("crash_log.txt")
    print("Saving log to path: \(logFilePath.path)") // 打印保存路径

    var fullLog = log
    if let additionalInfo = additionalInfo {
        fullLog = "\n\n\(additionalInfo)\n\n\(log)"
    }

    if FileManager.default.fileExists(atPath: logFilePath.path) {
        if let fileHandle = try? FileHandle(forWritingTo: logFilePath) {
            fileHandle.seekToEndOfFile()
            if let data = fullLog.data(using: .utf8) {
                fileHandle.write(data)
            }
            fileHandle.closeFile()
        } else {
            print("Failed to open file handle for \(logFilePath.path)")
        }
    } else {
        do {
            try fullLog.write(to: logFilePath, atomically: true, encoding: .utf8)
        } catch {
            print("Failed to write log to file: \(error)")
        }
    }
    print("Crash log saved at: \(logFilePath.path)")
}
 
func registerSignalHanlder() {
    objc_setUncaughtExceptionHandler { (_response: Any?) in
        if let signal = _response as? Int32 {
            signalHandler(signal: signal)
        } else if let exception = _response as? NSException {
            print("++++++++++++++++++ 异常信息 ++++++++++++++++++")
            NSLog("name:%@", exception.name.rawValue)
            NSLog("reason:%@", exception.reason ?? "--")
            NSLog("userInfo:%@", exception.userInfo ?? [:])
            NSLog("Stack Trace:%@", exception.callStackSymbols)
            NSLog("Stack ReturnAddresses:\n%@", exception.callStackReturnAddresses)
            NSLog("Exception All:\n%@", exception)
            
            let additionalInfo = """
            ======= EXCEPTION =======
            Name: \(exception.name.rawValue)
            Reason: \(exception.reason ?? "--")
            UserInfo: \(exception.userInfo ?? [:])
            ========================
            """
            saveCrashLog("", additionalInfo: additionalInfo)
            
            crashCallBack?(exception.reason ?? exception.callStackSymbols.joined(separator: "\r\n"), {
                unregisterSignalHandler()
                exception.raise()
            })
        }
    }
    
    signal(SIGINT, signalHandler)
    signal(SIGSEGV, signalHandler) // 野指针，僵尸对象
    signal(SIGTRAP, signalHandler)
    signal(SIGABRT, signalHandler)
    signal(SIGILL, signalHandler)
    signal(SIGBUS, signalHandler)
    signal(SIGFPE, signalHandler)
    signal(SIGTERM, signalHandler)
    signal(SIGKILL, signalHandler) // CPU无法执行的代码(比如无效指令、除以0等)
    signal(SIGPIPE, signalHandler)
}
 
func unregisterSignalHandler()
{
    signal(SIGINT,  SIG_DFL);
    signal(SIGSEGV, SIG_DFL);
    signal(SIGTRAP, SIG_DFL);
    signal(SIGABRT, SIG_DFL);
    signal(SIGILL,  SIG_DFL);
    signal(SIGBUS,  SIG_DFL);
    signal(SIGFPE,  SIG_DFL);
    signal(SIGTERM, SIG_DFL);
    signal(SIGKILL, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);
}
 
public class YMLogSwift
{
    public static func setup(callBack:@escaping CrashCallback){
        crashCallBack = callBack;
        registerSignalHanlder();
    }
}



